﻿using System.Collections.Generic;
using System.Threading.Tasks;
using Language.Utility;
using MsText = Microsoft.VisualStudio.Text;

namespace Language.Parser
{
    internal class ChangeMonitor
    {
        #region Private Data

        private Stub.Database _database;
        private lexer.managed.Lexer _lexer = new lexer.managed.Lexer();

        #endregion Private Data

        #region C-Tor

        public ChangeMonitor( Stub.Database database )
        {
            _database = database;
        }

        #endregion C-Tor

        #region Public Methods

        public void Attach( MsText.ITextBuffer textBuffer )
        {
            textBuffer.Changed += TextBuffer_Changed;
        }

        #endregion Public Methods

        #region Private Methods

        private async void TextBuffer_Changed( object sender, MsText.TextContentChangedEventArgs e )
        {
            string path = e.After.TextBuffer.ExtractFilename();
            FilePath processedPath = new FilePath() { Absolute = path };
            string relativeFilePath = processedPath.Relative;

            await Task.Run( () =>
            {

                Listener listener = new Listener();
                Local parser = new Local( _database, relativeFilePath );

                SortedSet<int> parsedLines = new SortedSet<int>();

                foreach ( var change in e.Changes )
                {
                    // For now, we ignore multi-line changes
                    if ( change.LineCountDelta == 0 )
                    {
                        MsText.ITextSnapshotLine line = e.After.GetLineFromPosition( change.NewPosition );

                        if ( !parsedLines.Contains( line.LineNumber ) )
                        {
                            string lineText = line.GetText();

                            listener.Reset( line.Start.Position, line.LineNumber );
                            _lexer.Tokenize( lineText, listener );

                            parser.Parse( listener.Tokens );

                            if ( !parser.Results.Empty )
                            {
                                ParseComplete?.Invoke( parser.Results, line.Extent, relativeFilePath );
                            }

                            parsedLines.Add( line.LineNumber );
                        }
                    }
                }
            } );
        }

        #endregion Private Methods

        #region Events

        public delegate void NewTokensParsedHandler( Results results, MsText.SnapshotSpan span, string relativeFilePath );
        public event NewTokensParsedHandler ParseComplete;

        #endregion Events
    }
}
